﻿using OurMind.Controls.Connectors;
using OurMind.Controls.Primitives;
using OurMind.Core;
using OurMind.Core.UnionCollection;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Emit;
using System.Security.Cryptography;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace OurMind.Controls
{
    ///<summary>思维导图的主要面板。</summary>
    public partial class MindPanel : Canvas
    {
        #region consts

        ///<summary>最小的视图缩放比。</summary>
        private const double MinViewScale = 0.1d;

        #endregion

        #region fields

        private MindGraph _mindGraph = new MindGraph();
        private UnionIndexer<UIElement> _uiElementIndexer;
        private CollectionChangedListener<MindConnect> _mindConnectCollectionChangedListener = new CollectionChangedListener<MindConnect>();
        private ConnectorManager<TestLineConnector> _connectorManager = new ConnectorManager<TestLineConnector>();

        #endregion

        #region temps

        // 在控件的位置。
        private Point _dragStartOffset;

        private IEnumerable<Tuple<MindConnect, UIElement>> _fromList;
        private IEnumerable<Tuple<MindConnect, UIElement>> _toList;
        private UIElement _current;

        #endregion

        #region dp props

        ///<summary>视图的缩放比。</summary>
        public double ViewScale { get { return (double)GetValue(ViewScaleProperty); } set { SetValue(ViewScaleProperty, value); } }
        public static readonly DependencyProperty ViewScaleProperty = DependencyProperty.Register("ViewScale", typeof(double), typeof(MindPanel), new FrameworkPropertyMetadata(1d)
        {
            CoerceValueCallback = ViewScaleCoerceValue,
            AffectsArrange = true,
        });

        ///<summary>视图呈现的视野信息。</summary>
        public Point ViewOffset { get { return (Point)GetValue(ViewOffsetProperty); } set { SetValue(ViewOffsetProperty, value); } }
        public static readonly DependencyProperty ViewOffsetProperty =
        DependencyProperty.Register("ViewOffset", typeof(Point), typeof(MindPanel), new FrameworkPropertyMetadata(new Point())
        {
            AffectsArrange = true,
        });

        ///<summary>连接符信息</summary>
        public IEnumerable<MindConnect> Connectors { get { return (IEnumerable<MindConnect>)GetValue(ConnectorsProperty); } set { SetValue(ConnectorsProperty, value); } }

        public static readonly DependencyProperty ConnectorsProperty =
        DependencyProperty.Register("Connectors", typeof(IEnumerable<MindConnect>), typeof(MindPanel), new PropertyMetadata(null)
        {
            CoerceValueCallback = ConnectorsCoerceValue,
            PropertyChangedCallback = ConnectorsPropertyChanged
        });


        #endregion

        #region ctors

        static MindPanel()
        {
            if (MinViewScale <= 0) throw new ArgumentOutOfRangeException($"MindPanel.static ctor: 常量 MinViewScale {MinViewScale} 必须大于零");
        }

        public MindPanel()
        {
            _mindConnectCollectionChangedListener.CollectionChanged += _mindConnectCollectionChangedListener_CollectionChanged;

            _uiElementIndexer = new UnionIndexer<UIElement>();

            var connectorGetter = new HandledUnionCollection<UIElement>(() => _connectors);
            var childrenGetter = new HandledUnionCollection<UIElement>(() => _sortedChildren);

            _uiElementIndexer.Add(connectorGetter);
            _uiElementIndexer.Add(childrenGetter);

            EventManager.RegisterClassHandler(typeof(MindPanel), Thumb.DragStartedEvent, new RoutedEventHandler(OnThumbDragStartCallback));
            EventManager.RegisterClassHandler(typeof(MindPanel), Thumb.DragDeltaEvent, new RoutedEventHandler(OnThumbDragDeltaCallback));

            //Test();
        }

        private void OnThumbDragStartCallback(object sender, RoutedEventArgs e)
        {
            var control = e.Source as UIElement;
            if (control == null) return;
            var hasId = GetHasID(control);

            _current = control;
            var id = GetID(control);

            var controls = Children.OfType<UIElement>().Where(c => GetHasID(c)).ToDictionary(c => GetID(c), c => c);
            _fromList = Connectors.Where(c => c.ToID == id).Where(c => controls.ContainsKey(c.FromID)).Select(c => Tuple.Create(c, controls[c.FromID]));
            _toList = Connectors.Where(c => c.FromID == id).Where(c => controls.ContainsKey(c.ToID)).Select(c => Tuple.Create(c, controls[c.ToID]));

            _dragStartOffset = Mouse.GetPosition(control);

        }

        private void OnThumbDragDeltaCallback(object sender, RoutedEventArgs e)
        {
            var control = e.Source as UIElement;
            if (control == null) return;
            var hasId = GetHasID(control);
            if (hasId == false) return;

            var pos = Mouse.GetPosition(this);
            var newPos = pos - new Vector(_dragStartOffset.X, _dragStartOffset.Y);

            Canvas.SetLeft(control, newPos.X);
            Canvas.SetTop(control, newPos.Y);

            foreach (var fromItem in _fromList)
            {
                var connect = fromItem.Item1;
                var from = fromItem.Item2;

                var fromControl = from;
                var toControl = control;

                var fromRect = GetControlRect(fromControl);
                var toRect = GetControlRect(toControl);

                var newConnector = _connectorManager.GetConnector(connect);
                _connectorManager.ArrangeConnector(newConnector, Orientation.Horizontal, GetStartPoint(fromRect), GetEndPoint(toRect));

            }

            foreach (var toItem in _toList)
            {
                var connect = toItem.Item1;
                var to = toItem.Item2;

                var fromControl = control;
                var toControl = to;

                var fromRect = GetControlRect(fromControl);
                var toRect = GetControlRect(toControl);

                var newConnector = _connectorManager.GetConnector(connect);
                _connectorManager.ArrangeConnector(newConnector, Orientation.Horizontal, GetStartPoint(fromRect), GetEndPoint(toRect));
            }
        }

        private void InitCreateConnectors()
        {
            // 从传入信息中获得连接信息，并且从中创建出指定的连接符对象

            var controls = Children.OfType<UIElement>().Where(c => GetHasID(c)).ToDictionary(c => GetID(c), c => c);

            foreach (var connector in Connectors)
            {
                var fromid = connector.FromID;
                var toid = connector.ToID;
                if (controls.ContainsKey(fromid) == false) continue;
                if (controls.ContainsKey(toid) == false) continue;

                var fromControl = controls[fromid];
                var toControl = controls[toid];

                var fromRect = GetControlRect(fromControl);
                var toRect = GetControlRect(toControl);

                var newConnector = _connectorManager.CreateConnector(connector);
                ConnectorsListAdd(newConnector);
                _connectorManager.ArrangeConnector(newConnector, Orientation.Horizontal, GetStartPoint(fromRect), GetEndPoint(toRect));
            }
        }

        private static Point GetEndPoint(Rect toRect)
        {
            return new Point(toRect.Left, toRect.Top + toRect.Height / 2);
        }

        private static Point GetStartPoint(Rect fromRect)
        {
            return new Point(fromRect.Right, fromRect.Top + fromRect.Height / 2);
        }


        #endregion

        #region visuals

        private ICollection<UIElement> _sortedChildren;

        ///<summary>连接符。</summary>
        private List<UIElement> _connectors = new List<UIElement>();

        protected override int VisualChildrenCount { get { return _uiElementIndexer.GetCount(); } }
        protected override Visual GetVisualChild(int index)
        {
            return _uiElementIndexer.GetElementAt(index);
        }

        #endregion

        #region dp props callback methods

        ///<summary>视图的缩放比默认不小于<see cref="MinViewScale"/>。</summary>
        private static object ViewScaleCoerceValue(DependencyObject d, object baseValue)
        {
            if (baseValue is double newViewScale) return Math.Max(newViewScale, MinViewScale);
            else return MinViewScale;
        }

        private static object ConnectorsCoerceValue(DependencyObject d, object baseValue)
        {
            if (d is MindPanel mindPanel)
            {
                if (baseValue is IEnumerable<MindConnect>)
                {
                    return baseValue;
                }
            }
            return null;
        }

        ///<summary>连接信息发生更改时会发生的回调。</summary>
        private static void ConnectorsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is MindPanel mindPanel)
            {
                mindPanel._mindConnectCollectionChangedListener.SetSource(e.NewValue as IEnumerable<MindConnect>);
            }
        }

        #endregion

        #region events callbacks

        private void _mindConnectCollectionChangedListener_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (IsInitialized == false) return;
            switch (e.Action)
            {
                // 在原列表新增。
                case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
                    break;

                // 在原列表删除。
                case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
                    break;

                // 在原列表替换信息
                case System.Collections.Specialized.NotifyCollectionChangedAction.Replace:
                    break;

                // 在原列表移动下标时
                case System.Collections.Specialized.NotifyCollectionChangedAction.Move:
                    break;

                // 重置或者清空原列表时。
                case System.Collections.Specialized.NotifyCollectionChangedAction.Reset:
                    break;
                default:
                    break;
            }
        }


        #endregion

        #region overrides

        public override void EndInit()
        {
            base.EndInit();
            InitCreateConnectors();

        }


        private Rect GetControlRect(UIElement element)
        {
            var left = Canvas.GetLeft(element);
            var top = Canvas.GetTop(element);
            var width = (double)element.GetValue(FrameworkElement.WidthProperty);
            var height = (double)element.GetValue(FrameworkElement.HeightProperty);
            return new Rect(left, top, width, height);
        }


        protected override Size MeasureOverride(Size constraint)
        {
            return base.MeasureOverride(constraint);
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
        }

        protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
        {
            base.OnVisualChildrenChanged(visualAdded, visualRemoved);

            if (visualAdded is not BaseConnector)
            {
                if (visualAdded is UIElement added)
                {
                    var hasID = GetHasID(added);
                    if (hasID == true)
                    {
                        // TODO 添加指定 ID 对应的连接符。
                    }
                }
            }
            //Debug.WriteLine("OnVisualChildrenChanged");

            if (visualRemoved is not BaseConnector)
            {
                if (visualRemoved is UIElement removed)
                {
                    var hasID = GetHasID(removed);
                    if (hasID == true)
                    {
                        // TODO 删除指定 ID 对应的连接符。
                    }
                }
            }

            UpdateSortedChildren();
        }

        #endregion


        #region test


        private void Test()
        {
            var rect = new Rectangle();
            ConnectorsListAdd(rect);
            rect.Width = 100;
            rect.Height = 100;
            rect.IsHitTestVisible = false;
            rect.IsManipulationEnabled = false;
            rect.Fill = Brushes.Green;
            rect.Arrange(new Rect(0, 0, 100, 100));
        }


        #endregion

        #region visual methods

        private void ConnectorsListAdd(UIElement element)
        {
            _connectors.Add(element);
            AddVisualChild(element);
        }

        private void ChildrenCollectionAdd(UIElement element)
        {
            Children.Add(element);
            UpdateSortedChildren();
        }

        private void UpdateSortedChildren()
        {
            var elements = Children.OfType<UIElement>();
            _sortedChildren = elements.OrderBy(e => Panel.GetZIndex(e)).ToList();
        }

        #endregion
    }
}
